/**
 * @file URL处理工具函数
 * @description 提供URL格式化、验证、比较和应用内部URL管理的工具函数
 * @author 黄敏
 * @version 2.0.0
 * @since 2025-06-27
 * @updated 2025-06-29 合并 url-manager 功能
 */

import { NETWORK_CONFIG } from '../constants'

// ==================== 类型定义 ====================

/**
 * URL解析结果接口
 * @description 用于解析特殊协议URL的结果
 */
export interface ParsedUrl {
  /** 协议名称 */
  protocol: string
  /** 路径部分 */
  path: string
  /** 查询参数对象 */
  query: Record<string, string>
  /** 哈希部分 */
  hash: string
}

/**
 * 格式化URL
 * 将各种格式的URL转换为可访问的URL
 * @param url - 要格式化的URL
 * @returns 格式化后的URL
 * @example
 * ```ts
 * formatUrl('example.com') // 返回: 'https://example.com'
 * formatUrl('192.168.1.1') // 返回: 'http://192.168.1.1'
 * formatUrl('search term') // 返回: 'https://www.google.com/search?q=search term'
 * ```
 */
export function formatUrl(url: string): string {
  const { PROTOCOL_REGEX, DOMAIN_REGEX, IP_REGEX, DEFAULT_SEARCH_ENGINE } = NETWORK_CONFIG

  // 带了协议的地址
  if (PROTOCOL_REGEX.test(url)) {
    return url
  }

  // 没带协议的域名地址
  if (DOMAIN_REGEX.test(url)) {
    return 'https://' + url
  }

  // IP地址
  if (IP_REGEX.test(url)) {
    return 'http://' + url
  }

  // 没带协议也不是地址，认为是搜索
  if (url !== '') {
    return `${DEFAULT_SEARCH_ENGINE}${encodeURIComponent(url)}`
  }

  return url
}

/**
 * 验证URL是否有效
 * @param url - 要验证的URL
 * @returns 是否为有效URL
 */
export function isValidUrl(url: string): boolean {
  try {
    new URL(url)
    return true
  } catch {
    return false
  }
}

/**
 * 从URL中提取域名
 * @param url - 要提取域名的URL
 * @returns 域名，如果无法提取则返回空字符串
 */
export function extractDomain(url: string): string {
  try {
    const urlObj = new URL(url)
    return urlObj.hostname
  } catch {
    return ''
  }
}

/**
 * 检查URL是否为本地文件
 * @param url - 要检查的URL
 * @returns 是否为本地文件URL
 */
export function isLocalFileUrl(url: string): boolean {
  return url.startsWith('file://')
}

/**
 * 检查URL是否为空白页
 * @param url - 要检查的URL
 * @returns 是否为空白页
 */
export function isBlankUrl(url: string): boolean {
  return url === 'about:blank' || url === ''
}

/**
 * 创建带参数的URL
 * @description 基于基础URL和参数对象创建完整的URL字符串
 * @param baseURL - 基础URL字符串
 * @param params - 查询参数对象，键值对形式
 * @returns 包含查询参数的完整URL字符串
 * @throws {Error} 当baseURL格式无效时抛出错误
 * @example
 * ```ts
 * createURLWithParams('https://example.com/api', { id: '123', type: 'user' })
 * // 返回: 'https://example.com/api?id=123&type=user'
 *
 * createURLWithParams('file:///path/to/file.html', { theme: 'dark', lang: 'zh' })
 * // 返回: 'file:///path/to/file.html?theme=dark&lang=zh'
 *
 * createURLWithParams('https://example.com/api') // 不传参数
 * // 返回: 'https://example.com/api'
 * ```
 */
export function createURLWithParams(baseURL: string, params?: Record<string, string>): string {
  try {
    const url = new URL(baseURL)

    if (params && Object.keys(params).length > 0) {
      const searchParams = new URLSearchParams(params)
      url.search = searchParams.toString()
    }

    return url.toString()
  } catch (error: unknown) {
    console.error('创建带参数的URL时出错:', error)
    throw new Error(`创建URL失败: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * 从URL中获取单个查询参数
 * @description 从指定URL字符串中提取查询参数值
 * @param url - URL字符串
 * @param key - 参数名
 * @param defaultValue - 默认值（可选，默认为空字符串）
 * @returns 参数值或默认值
 * @example
 * ```ts
 * getUrlParam('https://example.com?name=张三&age=25', 'name')
 * // 返回: '张三'
 *
 * getUrlParam('https://example.com?name=张三', 'city', '北京')
 * // 返回: '北京'
 * ```
 */
export function getUrlParam(url: string, key: string, defaultValue = ''): string {
  try {
    const urlObj = new URL(url)
    return urlObj.searchParams.get(key) || defaultValue
  } catch (error) {
    console.warn('解析URL参数失败:', error)
    return defaultValue
  }
}

/**
 * 从URL中获取所有查询参数
 * @description 从指定URL字符串中提取所有查询参数，返回键值对对象
 * @param url - URL字符串
 * @returns 参数对象，重复参数只返回第一个值
 * @example
 * ```ts
 * getAllUrlParams('https://example.com?name=张三&age=25')
 * // 返回: { name: '张三', age: '25' }
 * ```
 */
export function getAllUrlParams(url: string): Record<string, string> {
  try {
    const urlObj = new URL(url)
    const params: Record<string, string> = {}
    for (const [key, value] of urlObj.searchParams.entries()) {
      params[key] = value
    }
    return params
  } catch (error) {
    console.warn('解析URL参数失败:', error)
    return {}
  }
}

/**
 * 检查URL是否包含指定参数
 * @description 检查URL中是否存在指定的查询参数
 * @param url - URL字符串
 * @param key - 参数名
 * @returns 是否存在该参数
 * @example
 * ```ts
 * hasUrlParam('https://example.com?name=张三&age=25', 'name')
 * // 返回: true
 *
 * hasUrlParam('https://example.com?name=张三', 'city')
 * // 返回: false
 * ```
 */
export function hasUrlParam(url: string, key: string): boolean {
  try {
    const urlObj = new URL(url)
    return urlObj.searchParams.has(key)
  } catch (error) {
    console.warn('解析URL参数失败:', error)
    return false
  }
}

// ==================== 应用内部URL管理 ====================

/**
 * URL模式正则表达式
 * @description 用于匹配和解析不同类型的URL
 */
const URL_PATTERNS = {
  /** 特殊协议URL模式 (f2://path?query#hash) */
  specialUrl: /^f2:\/\/([^?#]+)(?:\?(.*?))?(?:#(.*))?$/,
  /** 访问URL模式 (匹配渲染器窗口路径) */
  accessUrl: /srcRenderer[/\\]windows[/\\]([^/]+)[/\\]index.html(?:\\?(.*?))?(?:#(.*))?$/
} as const

/**
 * 解析特殊协议URL
 * @description 解析 f2:// 协议的URL，提取路径、查询参数和哈希
 * @param url - 要解析的特殊URL
 * @returns 解析结果，如果解析失败则返回null
 * @example
 * ```ts
 * parseSpecialUrl('f2://settings?theme=dark#appearance')
 * // 返回: { protocol: 'f2', path: 'settings', query: { theme: 'dark' }, hash: 'appearance' }
 * ```
 */
export function parseSpecialUrl(url: string): ParsedUrl | null {
  try {
    const match = url.match(URL_PATTERNS.specialUrl)
    if (!match) return null

    const [, pathRaw, queryString = '', hash = ''] = match
    const path = pathRaw ?? ''
    const query: Record<string, string> = {}

    if (queryString) {
      for (const param of queryString.split('&')) {
        const [key, value] = param.split('=')
        if (key) query[key] = decodeURIComponent(value || '')
      }
    }

    return {
      protocol: 'f2',
      path,
      query,
      hash: decodeURIComponent(hash)
    }
  } catch (error) {
    console.error('解析特殊 URL 时出错:', error)
    return null
  }
}
